#pragma once

#include <QWidget>
#include <QDebug>
#include <QTimer>
#include <mutex>
#include <queue>

#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/visualization/range_image_visualizer.h>
#include <pcl/compression/octree_pointcloud_compression.h>

#include <opencv2/opencv.hpp>

#include "LidarDataManager.h"
#include "LidarConfigurationOptions.h"
#include "TcpServerSender.h"

#include <vtkRenderWindow.h>
#include <QVBoxLayout>

using namespace pcl::visualization;

class DemoWidget : public QWidget
{
    Q_OBJECT
public:
    DemoWidget(QWidget *parent = nullptr);
    ~DemoWidget();

    pcl::PointCloud<pcl::PointXYZRGB>::Ptr getImagePointCloud(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, cv::Mat &image);

private:
    void setupUI();

    QVTKOpenGLWidget *vtkWidget;
    boost::shared_ptr<PCLVisualizer> pcl_viewer;
    boost::shared_ptr<RangeImageVisualizer> pcl_viewer_range;

    std::shared_ptr<pcl::PointCloud<pcl::PointXYZI>> cloud;
    QTimer *timer;
    std::queue<PointCloudData<pcl::PointXYZI>> cloud_queue;
    std::mutex mtx_viewer;

    ILidarDataManager *_dataManager = nullptr;
    LidarConfigurationOptions *_option = nullptr;

    bool processData(PointCloudData<pcl::PointXYZI> &data);

    void TestReadPCD();
    void TestReadStream();
    void TestImage();

    double lastTimeStamp = 0;
    double startTimeStamp = 0;
    int encodeNum = 0;
    std::stringstream _stream;
    pcl::io::OctreePointCloudCompression<pcl::PointXYZ> *_encoder = nullptr;
    pcl::io::OctreePointCloudCompression<pcl::PointXYZ> *_decoder = nullptr;
    std::vector<std::pair<uint64_t, std::streampos>> stream_index;

    cv::Mat image;
    cv::Mat image_write;
    bool isUpdatingDisplay = false;
    bool isSavedStream = false;
    bool isSendStream = false;
    bool isSavedPng = false;

    void InitTCP();
    void DisposeTCP();
    pcl::io::OctreePointCloudCompression<pcl::PointXYZ> *_encoder_tcp = nullptr;
    TcpServerSender *_tcpServerSender = nullptr;

private slots:
    void updateViewer();
    void updateSavePCD();
    void updateSaveStream();
    void updateSendStream();
};